// ECE 498 AVR ADC Ohmmeter + State Machine
// Craig Verrill, Tim Bremm, Matt Edwards

#include "config.h"
//#include <avr/iom8.h>
//#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//#define F_CPU 1000000UL
#include <util/delay.h>
#define input1 (PINC & (1<<PINC1))	// To make input pin stuff easier
//#define FOSC 1000000		// Oscillator Value (1Mhz)
#define Rdiv 9840		// This is our known ohmmeter resistor to ground

// Function declarations - These are commented at the functions themselves
void init_serial(void);
void my_send_string(char *buffer);
void init_ad(void);
int adc(void);
void outputohms(void);
void state1(void);
void state2(void);

char buf[50];			// Buffer used for serial communication
int prior_input;		// For using the switch for the state machine
int flag = 1;			// Global flag so waiting screen doesn't flood terminal

int menu(void)
{
	init_serial();		// Initialize serial communication
	init_ad();		// Initialize ADC
	DDRC |= (1 << DDC1);	// Make pin 1 of port C an input for switches
	prior_input = input1;	// Save current switch state
	sprintf(buf, "Welcome!\n\r");	// Store welcome message into send buffer
	my_send_string(buf);	// Send message via serial

	while (1) {
		if (flag) {	// Say waiting if it hasn't already been said this round
			sprintf(buf, "Waiting for input\n\r");	// Let user know how patient we are
			my_send_string(buf);
			flag = 0;	// Make sure we don't flood the screen with repeat messages
		}
		if (input1 != prior_input) {	// If the switch has been toggled
			_delay_ms(50);	// software debouncing of the switch
			prior_input = input1;	// save current switch state
			state1();	// Enter state machine
			flag = 1;	// Once done with state machine, enable waiting prompt
		}
	}
	return 0;
}

// State1 is entered if the switch has been toggled.
// It then checks for another toggle every 1/4 second
// and if it doesn't get a toggle, it times out and 
// goes back to waiting in the main loop. If the switch
// is flipped within about 2 seconds, then it will enter
// into state2.
void state1(void)
{
	sprintf(buf, "Press me once, shame on you...\n\r");
	my_send_string(buf);	// acknowledge that you've entered state1
	int x = 0;
	while (x < 9) {
		_delay_ms(250);
		if (input1 != prior_input) {	// look for switch toggle
			_delay_ms(50);	// software debounce
			prior_input = input1;	// save current switch state
			x = 10;	// make sure we drop out of while/delay loop
			state2();	// enter into state2
		}
		x++;
	}
	_delay_ms(50);		// switch debounce
	prior_input = input1;	// save current state before returning
	return;
}

// State2 does the same thing as state1, except that if
// the switch is toggled before the timeout, it will
// enter ohmmeter mode.

void state2(void)
{
	sprintf(buf, "Press me twice, shame on me...\n\r");
	my_send_string(buf);	// acknowledge that you've entered state2
	int x = 0;
	while (x < 9) {
		_delay_ms(250);
		if (input1 != prior_input) {	// look for switch toggle
			_delay_ms(50);	// software debounce
			prior_input = input1;	// save current switch state
			x = 10;	// make sure we drop out of while/delay loop
			outputohms();	// Enter into ohmmeter mode
		}
		x++;
	}
	_delay_ms(50);		// switch debounce
	prior_input = input1;	// save current state before returning
	return;
}
#if 0
// Outputohms gets a value from the ADC, and decides
// if it's a resistance, an open, or a short, and outputs accordingly.
void outputohms(void)
{
	int resistance, volt;
	double outval;
	volt = adc();		// Get the ADC output value (int between 0 & 1023, inclusive
	if (!volt) {		// If there is an open circuit output "inf"
		sprintf(buf, "The ADC output value is: inf\n\r");
		my_send_string(buf);
	} else if (volt < 1023) {	// If there's a resistor, calculate its value and output via serial
		outval = ((Rdiv * 1024.0) / volt) - Rdiv;	// Convert ADC value to resistance (floating point operation)
		resistance = outval;	// put above answer into int form because sprintf likes it better
		sprintf(buf,
			"The ADC output value is: %d\n\rThe resistance is: %d ohms\n\n\r",
			volt, resistance);
		my_send_string(buf);	// output the raw ADC value and calculated resistance
	} else {		// If the ADC is shorted, let us know
		sprintf(buf,
			"The ADC output value is: %d\n\rThe resistance is: futile... I mean zero.\n\r\n\r",
			volt);
		my_send_string(buf);
	}
}


/* Initializes AVR USART for 9600 baud (assuming 1MHz clock) */
/* 1MHz/(8*(12+1)) = 9600 ish                                */
void init_ad(void)
{
	ADMUX |= (1 << REFS0);	// Set to compare against 5V
	ADMUX &= ~(1 << REFS1);	// Also does this, so comparison is vs. Vcc with ext. cap on Vref

	ADMUX &= ~(1 << MUX0);	// These statements determine that we read from ADC0
	ADMUX &= ~(1 << MUX1);
	ADMUX &= ~(1 << MUX2);
	ADMUX &= ~(1 << MUX3);	// Read ADC from ADC0 only
	//Set the prescaler clock to divide by 8.
	ADCSRA |= (1 << ADPS1);
	ADCSRA |= (1 << ADPS0);
	ADCSRA &= ~(1 << ADPS2);

	ADCSRA |= (1 << ADIE);	// Enable ADC conversion interrupt
	// Defaults to these(ADIE and ADFR), but we want to make sure
	ADCSRA &= ~(1 << ADFR);	// Make sure free running mode is disabled
};

// This initializes the ADC. First it turns it on, then begins the conversion.
// It waits until the conversion finishes (which automatically clears the ADC interrupt),
// and then it automatically resets the interrupt. The low ADC register value must
// be read first, because once ADCL is read, ADC access to the data register is
// blocked. Once ADCH is read, access is reenabled.
int adc(void)
{
	int value, low, high;
	ADCSRA |= (1 << ADEN);	// Enable ADC
	ADCSRA |= (1 << ADSC);	//Begin AD conversion (cleared by hardware when complete)
	while (!(ADCSRA & (1 << ADIF)));	//Wait for ADC Interrupt
	low = ADCL;		// Must read low byte first to make sure that
	high = ADCH;		// both bytes are from same conversion
	// reading ADCH reenables ADC access to data register
	value = (high << 8) + low;	// Shift MSB's into their proper position and append the LSB's
	ADCSRA &= ~(1 << ADEN);	// Disable ADC to conserve power
	return value;		// Return the raw ADC value.
}

// Initializes serial communication with 9600 baud, 8 data bits, 2 stop bits,
// and no parity, as well as enabling the transmit and recieve bits.
void init_serial(void)
{
	UBRRH &= ~(1 << URSEL);	//Clear URSEL bit in UBRRH reg. before writing to UBRRH/UBRRL   
	UCSRA |= (1 << U2X);	// u2x = 1 & UBRRL = 12 means baud = 9600
	UBRRH = 0;		// set baud MSB for 9600
	UBRRL = 12;		// 9600 BAUD FOR 1MHZ SYSTEM CLOCK
	UBRRH |= (1 << URSEL);	//      To write to the UCSRC Register, the URSEL bit (MSB) must be set due to
	// the sharing of I/O location by UBRRH and UCSRC
	UCSRC |= (1 << URSEL) | (1 << USBS) | (1 << UCSZ0) | (1 << UCSZ1);	// 8 DATA BIT 2 STOP BIT
	UCSRC &= (~(1 << UPM1)) & (~(1 << UPM0));	//DISABLE PARITY

	UCSRB = (1 << RXEN) | (1 << TXEN);	// ENABLE TX AND RX ALSO 8 BIT
	UCSRB &= ~(1 << UCSZ2);	// continue setting 8 data bits
}

/* simple routine to use software polling to send a string serially */
/* waits for UDRE (USART Data Register Empty) before sending byte   */
/* uses strlen to decide how many bytes to send (must have null     */
/* terminator on the end of the string).                            */

void my_send_string(char *buffer)
{
	int x;			//uses software polling, assumes serial is set up  
	for (x = 0; x < strlen(buffer); x++)	// Iterate until the end of the string as determined by strlen()
	{
		while (!(UCSRA & (1 << UDRE)));	// wait until send register is available 
		UDR = buffer[x];	// write out data via serial
	}
	return;
}
#endif
